#include <objHandle.h>
#include <stdlib.h>
#include <string.h>

#define OBJ_HANDLE_SMALLEST_GENNUM  (0x10000U)                       /* smallest genNum is 0x10000 */
#define OBJ_HANDLE_INDEX_BITS       16                               /* bit count for handle index */
#define OBJ_HANDLE_GEN_BITS         15                               /* bit count for generation nunmber */
#define OBJ_HANDLE_ENTRY_BITS       8                                /* portion of index used for */
#define OBJ_HANDLE_INDEX_CNT        (1 << OBJ_HANDLE_INDEX_BITS)     /* 00010000 */
#define OBJ_HANDLE_TBL_SIZE         (1 << OBJ_HANDLE_ENTRY_BITS)     /* 256 */
#define OBJ_MASTER_TBL_SIZE         (OBJ_HANDLE_INDEX_CNT / OBJ_HANDLE_TBL_SIZE)
#define OBJ_INDEX_GET(objHdl)       ((objHdl) & (OBJ_HANDLE_INDEX_CNT - 1))
#define OBJ_ENTRY_INDEX_GET(objHdl) ((objHdl) & (OBJ_HANDLE_TBL_SIZE - 1))
#define OBJ_TABLE_INDEX_GET(objHdl) (OBJ_INDEX_GET(objHdl) >> OBJ_HANDLE_ENTRY_BITS)
#define OBJ_HANDLE_GEN_INCR         OBJ_HANDLE_INDEX_CNT                                       /* 00010000 */
#define OBJ_HANDLE_GEN_MAX          (((1 << OBJ_HANDLE_GEN_BITS) - 1) << OBJ_HANDLE_INDEX_BITS) /* 7fff0000 */

typedef unsigned int UINT32;
typedef unsigned int UINT;

typedef struct handle_table_entry
{
    DL_NODE    handleNode;
    union
    {
        OBJ_ID entryObjId;    /* if allocated: object ID */
        int    nextFree;      /* if free: link to next free entry */
    } u;
    OBJ_HANDLE objHandle;     /* object handle */
} HANDLE_TABLE_ENTRY;

typedef struct handle_table
{
    HANDLE_TABLE_ENTRY handleTable[OBJ_HANDLE_TBL_SIZE];
    struct master_handle_table* pMasterTbl;
} HANDLE_TABLE;

typedef struct master_handle_table
{
    HANDLE_TABLE* pHandleTable[OBJ_MASTER_TBL_SIZE];
    int           firstFree; /* index of most recent free slot */
    UINT32        size;      /* size of all handle tables combined */
    UINT32        initIndex;
    UINT32        genNumber;
} MASTER_HANDLE_TABLE;
HANDLE_TABLE handleTable[OBJ_MASTER_TBL_SIZE];

static MASTER_HANDLE_TABLE mstrHandleTbl =
{ {0}, -1, OBJ_HANDLE_TBL_SIZE, 0, OBJ_HANDLE_SMALLEST_GENNUM };
static DL_LIST		objHandleList = {0,0};
static int objHandleTblCreate()
{
    HANDLE_TABLE* pHTable;
    pHTable = (HANDLE_TABLE*)malloc(sizeof(HANDLE_TABLE));

    if (pHTable == NULL)
        return (ERROR);

    (void)memset((void*)pHTable, 0, sizeof(HANDLE_TABLE));
    pHTable->pMasterTbl = &mstrHandleTbl;
    mstrHandleTbl.pHandleTable[0] = pHTable;

    return (OK);
}
OBJ_HANDLE objHandleAlloc(OBJ_ID objId)
{
    MASTER_HANDLE_TABLE* pMstrTable;
    HANDLE_TABLE* pHTable;
    OBJ_HANDLE objHandleRet;
    UINT index;
    UINT entryIx;
    static int firstFalg = ERROR;
    pMstrTable = &mstrHandleTbl;

    if(firstFalg == ERROR)
    {
        objHandleTblCreate();
        firstFalg = OK;
    }
    
objHdlAllocTryAgain:

    if (pMstrTable->initIndex < pMstrTable->size)
    {
        index = pMstrTable->initIndex++;
        entryIx = OBJ_ENTRY_INDEX_GET(index);
        pHTable = pMstrTable->pHandleTable[OBJ_TABLE_INDEX_GET(index)];
    }
    else if (pMstrTable->firstFree == -1)
    {
        if ((pMstrTable->firstFree != -1) ||
            (pMstrTable->initIndex < pMstrTable->size))
            goto objHdlAllocTryAgain;

        if (0 == OBJ_TABLE_INDEX_GET(pMstrTable->size))
            return (0);

        pHTable = (HANDLE_TABLE*)malloc(sizeof(HANDLE_TABLE));
        if (pHTable == NULL)
            return (0);

        memset((void*)pHTable, 0, sizeof(HANDLE_TABLE));
        pHTable->pMasterTbl = pMstrTable;
        pMstrTable->pHandleTable[OBJ_TABLE_INDEX_GET(pMstrTable->size)] = pHTable;
        pMstrTable->size += OBJ_HANDLE_TBL_SIZE;
        goto objHdlAllocTryAgain;
    }

    else
    {
        index = (UINT)pMstrTable->firstFree;
        entryIx = OBJ_ENTRY_INDEX_GET(index);
        pHTable = pMstrTable->pHandleTable[OBJ_TABLE_INDEX_GET(index)];
        pMstrTable->firstFree = pHTable->handleTable[entryIx].u.nextFree;
    }

      /*
       * Construct the object handle:
       *	 MSB	     32 bits           LSB
       *	 - - - - - - - - - - - - - - - - -
       *	| 1 bit    | 15 bits   | 16 bits |
       *	| reserved | genNumber | index   |
       *	 ---------------------------------
       */
    
    pHTable->handleTable[entryIx].objHandle = (OBJ_HANDLE)pMstrTable->genNumber;
    pHTable->handleTable[entryIx].objHandle |= (OBJ_HANDLE)index;

    pMstrTable->genNumber += OBJ_HANDLE_GEN_INCR;
    if (pMstrTable->genNumber == OBJ_HANDLE_GEN_MAX)
        pMstrTable->genNumber = OBJ_HANDLE_GEN_INCR;

    pHTable->handleTable[entryIx].u.entryObjId = objId;
    objHandleRet = pHTable->handleTable[entryIx].objHandle;
    dllAdd(&objId->objHandleList, 
        &pHTable->handleTable[entryIx].handleNode);
    return (objHandleRet);
}
OBJ_ID objHandleToObjId(OBJ_HANDLE handle)
{
    UINT index;
    int tblIndex;
    int entryIndex;
    HANDLE_TABLE* pHandleTbl;

    if (handle == 0)
        return (NULL);

    index = OBJ_INDEX_GET(handle);
    tblIndex = OBJ_TABLE_INDEX_GET(handle);
    entryIndex = OBJ_ENTRY_INDEX_GET(handle);

    if (index >= mstrHandleTbl.size)
        return (NULL);

    pHandleTbl = mstrHandleTbl.pHandleTable[tblIndex];
    if (handle != pHandleTbl->handleTable[entryIndex].objHandle)
        return (NULL);

    return pHandleTbl->handleTable[entryIndex].u.entryObjId;
}
STATUS objHandleClose(OBJ_HANDLE handle)
{

    UINT index;
    int entryIndex;
    HANDLE_TABLE* pHTable;

    index = OBJ_INDEX_GET(handle);
    entryIndex = OBJ_ENTRY_INDEX_GET(handle);

    if (index >= mstrHandleTbl.size)
        return (ERROR);

    pHTable = mstrHandleTbl.pHandleTable[OBJ_TABLE_INDEX_GET(handle)];

    if (pHTable->handleTable[entryIndex].objHandle != handle)
        return (ERROR);
    dllRemove(
        &pHTable->handleTable[entryIndex].u.entryObjId->objHandleList,
        &pHTable->handleTable[entryIndex].handleNode);
    pHTable->handleTable[entryIndex].objHandle = 0;
    pHTable->handleTable[entryIndex].u.nextFree =
        mstrHandleTbl.firstFree;
    mstrHandleTbl.firstFree = (int)index;

    return (OK);
}
void objHandleTblTerminate()
{
    UINT index;

    for (index = 0; index < OBJ_MASTER_TBL_SIZE; index++)
    {
        if (mstrHandleTbl.pHandleTable[index] != NULL)
            free((char*)mstrHandleTbl.pHandleTable[index]);
        else
            break;
    }
}

OBJ_HANDLE objIdToObjHandle(OBJ_ID objId)
{
    HANDLE_TABLE_ENTRY* pHEntry;

    pHEntry = (HANDLE_TABLE_ENTRY*)DLL_FIRST(&objId->objHandleList);
    if (pHEntry == NULL)
    {
        return (OK);
    }

    return pHEntry->objHandle;
}
